/*
 * FlowComponentDefinition.java
 *
 * Created on September 8, 2006, 4:42 PM
 *
 * (C) 2006 R. Alexander Milowski
 */

package com.smallx.xproc.library;

import com.smallx.xproc.ComponentType;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.infoset.component.Component;
import org.infoset.component.ComponentBase;
import org.infoset.component.ComponentDefinition;
import org.infoset.xml.Document;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;

/**
 *
 * @author alex
 */
public class FlowComponentDefinition implements ComponentDefinition {

   protected ComponentType type;
   protected Flow flow;
   
   /** Creates a new instance of FlowComponentDefinition */
   public FlowComponentDefinition(ComponentType type,Flow flow) {
      this.type = type;
      this.flow = flow;
   }
   
   protected Component newInstance(final List<ComponentInitializer> components) {
      return new ComponentBase(type.getName()) {
         public void init(Component.Context context) 
            throws XMLException
         {
            for (ComponentInitializer initializer : components) {
               initializer.setParentContext(context);
               initializer.init();
               initializer.bindStatics();
               initializer.bindOutputs();
            }
         }
         public void bindOutputPort(Name port,ItemDestination dest) 
            throws XMLException
         {
            Name outputName = type.getOutputPortNames().iterator().next();
            if (!port.equals(outputName)) {
               throw new XMLException("There is no output port named "+port+" on the pipeline "+type.getName());
            }
            //LOG.info("Binding port "+port+" on pipeline by binding "+flow.getOutputPort()+" on last component");
            ComponentInitializer end = components.get(components.size()-1);
            end.getComponent().bindOutputPort(flow.getOutputPort(),dest);
         }

         public void onStart()
            throws XMLException
         {
            for (ComponentInitializer initializer : components) {
               initializer.getComponent().onStart();
            }
         }

         public ItemDestination getInputPort(Name port) 
            throws XMLException
         {
            Name inputName = type.getInputPortNames().iterator().next();
            if (!port.equals(inputName)) {
               throw new XMLException("There is no input port named "+port+" on the pipeline "+type.getName());
            }
            ComponentInitializer start = components.get(0);
            return start.getComponent().getInputPort(flow.getInputPort());
         }

         public void onDocumentInput(Name port,Document doc) 
            throws XMLException
         {
            Name inputName = type.getInputPortNames().iterator().next();
            if (!port.equals(inputName)) {
               throw new XMLException("There is no input port named "+port+" on the pipeline "+type.getName());
            }
            ComponentInitializer start = components.get(0);
            start.getComponent().onDocumentInput(flow.getInputPort(),doc);
         }

         public void onEnd()
            throws XMLException
         {
            for (ComponentInitializer initializer : components) {
               initializer.getComponent().onEnd();
            }
         }

      };
   }
      
   public Component newInstance()
      throws XMLException 
   {
      ChainedComponent start = flow.get(0);
      ChainedComponent end = null;
      final List<ComponentInitializer> components = new ArrayList<ComponentInitializer>();
      for (ChainedComponent chained : flow) {
         ComponentDefinition def = chained.getComponent();
         Component c = def.newInstance();
         ComponentInitializer initializer = new ComponentInitializer(def.getName(),c);
         for (Name name : chained.getStaticallyBoundPorts()) {
            initializer.addStaticInput(name,chained.getStaticBinding(name));
         }
         if (components.size()>0) {
            ChainedComponent last = flow.get(components.size()-1);
            components.get(components.size()-1).addDestinationAdapter(
               new DestinationAdapter(last.getOutputPort(),chained.getInputPort(),c)
            );
         }
         components.add(initializer);
      }
      
      return newInstance(components);

   }

   public Name getName() {
      return type.getName();
   }

   public String getVersion() {
      return null;
   }

   public String getVendor() {
      return null;
   }

   public URI getVendorLocation() {
      return null;
   }

   public Collection<Name> getInputs() {
      return type.getInputPortNames();
   }

   public Collection<Name> getOutputs() {
      return type.getOutputPortNames();
   }

}

